/*
 * Copyright (c) 2012, Willow Garage, Inc.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the Willow Garage, Inc. nor the names of its
 *       contributors may be used to endorse or promote products derived from
 *       this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#include <QStringList>

#include <rviz/properties/quaternion_property.h>
#include <rviz/properties/property_tree_model.h>

namespace rviz
{
QuaternionProperty::QuaternionProperty(const QString& name,
                                       const Ogre::Quaternion& default_value,
                                       const QString& description,
                                       Property* parent)
  : Property(name, QVariant(), description, parent)
  , quaternion_(default_value)
  , ignore_child_updates_(false)
{
  x_ = new Property("X", quaternion_.x, "X coordinate", this);
  y_ = new Property("Y", quaternion_.y, "Y coordinate", this);
  z_ = new Property("Z", quaternion_.z, "Z coordinate", this);
  w_ = new Property("W", quaternion_.w, "W coordinate", this);
  updateString();
  connect(x_, &Property::aboutToChange, this, &QuaternionProperty::emitAboutToChange);
  connect(y_, &Property::aboutToChange, this, &QuaternionProperty::emitAboutToChange);
  connect(z_, &Property::aboutToChange, this, &QuaternionProperty::emitAboutToChange);
  connect(w_, &Property::aboutToChange, this, &QuaternionProperty::emitAboutToChange);
  connect(x_, &Property::changed, this, &QuaternionProperty::updateFromChildren);
  connect(y_, &Property::changed, this, &QuaternionProperty::updateFromChildren);
  connect(z_, &Property::changed, this, &QuaternionProperty::updateFromChildren);
  connect(w_, &Property::changed, this, &QuaternionProperty::updateFromChildren);
}

bool QuaternionProperty::setQuaternion(const Ogre::Quaternion& new_quaternion)
{
  if (new_quaternion != quaternion_)
  {
    Q_EMIT aboutToChange();
    quaternion_ = new_quaternion;
    ignore_child_updates_ = true;
    x_->setValue(quaternion_.x);
    y_->setValue(quaternion_.y);
    z_->setValue(quaternion_.z);
    w_->setValue(quaternion_.w);
    ignore_child_updates_ = false;
    updateString();
    Q_EMIT changed();
    if (model_)
      model_->emitDataChanged(this);
    return true;
  }
  return false;
}

bool QuaternionProperty::setValue(const QVariant& new_value)
{
  QStringList strings = new_value.toString().split(';');
  if (strings.size() >= 4)
  {
    bool x_ok = true;
    float x = strings[0].toFloat(&x_ok);
    bool y_ok = true;
    float y = strings[1].toFloat(&y_ok);
    bool z_ok = true;
    float z = strings[2].toFloat(&z_ok);
    bool w_ok = true;
    float w = strings[3].toFloat(&w_ok);
    if (x_ok && y_ok && z_ok && w_ok)
    {
      return setQuaternion(Ogre::Quaternion(w, x, y, z));
    }
  }
  return false;
}

void QuaternionProperty::updateFromChildren()
{
  if (!ignore_child_updates_)
  {
    quaternion_.x = x_->getValue().toFloat();
    quaternion_.y = y_->getValue().toFloat();
    quaternion_.z = z_->getValue().toFloat();
    quaternion_.w = w_->getValue().toFloat();
    updateString();
    Q_EMIT changed();
  }
}

void QuaternionProperty::emitAboutToChange()
{
  if (!ignore_child_updates_)
  {
    Q_EMIT aboutToChange();
  }
}

void QuaternionProperty::updateString()
{
  value_ = QString("%1; %2; %3; %4")
               .arg(quaternion_.x, 0, 'g', 5)
               .arg(quaternion_.y, 0, 'g', 5)
               .arg(quaternion_.z, 0, 'g', 5)
               .arg(quaternion_.w, 0, 'g', 5);
}

void QuaternionProperty::load(const Config& config)
{
  float x, y, z, w;
  if (config.mapGetFloat("X", &x) && config.mapGetFloat("Y", &y) && config.mapGetFloat("Z", &z) &&
      config.mapGetFloat("W", &w))
  {
    // Calling setQuaternion() once explicitly is better than letting
    // the Property class load the X, Y, Z, and W children
    // independently, which would result in at least 4 calls to
    // setQuaternion().
    setQuaternion(Ogre::Quaternion(w, x, y, z));
  }
}

void QuaternionProperty::save(Config config) const
{
  // Saving the child values explicitly avoids having Property::save()
  // save the summary string version of the property.
  if (getReadOnly())
    return;
  config.mapSetValue("X", x_->getValue());
  config.mapSetValue("Y", y_->getValue());
  config.mapSetValue("Z", z_->getValue());
  config.mapSetValue("W", w_->getValue());
}

void QuaternionProperty::setReadOnly(bool read_only)
{
  Property::setReadOnly(read_only);
  x_->setReadOnly(read_only);
  y_->setReadOnly(read_only);
  z_->setReadOnly(read_only);
  w_->setReadOnly(read_only);
}


} // end namespace rviz
